﻿#if UNITY_EDITOR
using System;
using System.IO;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEditor;
using System.Data;
using ExcelDataReader;
using Core;
using System.Text.RegularExpressions;

namespace Core
{
    public class ExcelEditor
    {
        public const string SHEET_NAME = "Sheet1";

        private static string configScriptsName;

        [MenuItem("Assets/Excel/CreateScript")]
        public static void CreateScript()
        {
            var prefix = "Assets";
            var path = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (path.EndsWith(".xls") || path.EndsWith(".xlsx"))
            {
                var fullPath = Application.dataPath + path.Substring(prefix.Length);
                using (FileStream stream = File.Open(fullPath, FileMode.Open))
                {
                    IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);

                    DataSet result = excelReader.AsDataSet();
                    var collect = result.Tables[SHEET_NAME].Rows;
                    if (collect == null)
                    {
                        Debug.LogError("默认读取" + SHEET_NAME + "表格，未找到改表格");
                        return;
                    }

                    var savePath = $"{Application.dataPath}/Scripts/Excel/Generate";
                    if (!Directory.Exists(savePath))
                        Directory.CreateDirectory(savePath);

                    try
                    {
                        var assetFullPath = $"{savePath}/{Selection.activeObject.name}.cs";
                        var fileName = Selection.activeObject.name;
                        var configName = Selection.activeObject.name.Replace("Excel", "") + "Config";
                        var templatePath = "Assets/Plugins/client-source/Editor/Excel/ExampleExcel.cs";
                        var content = File.ReadAllText(templatePath);

                        // 判断key类型
                        if (collect.Count < 3)
                        {
                            Debug.LogError("配置表没有配置字段类型");
                            return;
                        }

                        var typeStr = collect[2].ItemArray[0].ToString();

                        content = content.Replace("public partial class ExampleConfig : Config<int>", 
                            $"public partial class {configName} : Config<{typeStr}>");

                        content = content.Replace("public partial class ExampleExcel : ConfigExcel<ExampleExcel, ExampleConfig, int>",
                            $"public partial class {fileName} : ConfigExcel<{fileName}, {configName}, {typeStr}>");

                        var builder = new StringBuilder();
                        // 减4行，第一行字段名，第二行描述, 第三行类型，第4行拓展

                        var space = "    ";
                        var title = collect[0];
                        // 第一列固定为ID，继承中已经存在，不做处理
                        for (int i = 1; i < title.ItemArray.Length; i++)
                        {
                            var cur = title.ItemArray[i];
                            if (string.IsNullOrEmpty(cur.ToString()))
                                break;

                            var fieldName = cur.ToString();
                            var fieldDes = collect[1].ItemArray[i].ToString().Trim();
                            var fieldType = collect[2].ItemArray[i].ToString().Trim();
                            
                            builder.AppendLine($"{space}/// <summary>");
                            builder.AppendLine($"{space}/// {fieldDes}");
                            builder.AppendLine($"{space}/// </summary>");
                            builder.Append($"{space}public {fieldType} {fieldName};");
                            
                            // 空行
                            if (i != (title.ItemArray.Length - 1) && !string.IsNullOrEmpty(title.ItemArray[i + 1].ToString()))
                                builder.AppendLine();
                        }

                        content = content.Replace("//TODO", builder.ToString());

                        if (File.Exists(assetFullPath))
                            File.Delete(assetFullPath);
                        File.WriteAllText(assetFullPath, content);

                        AssetDatabase.Refresh();
                        Debug.Log("生成成功:" + assetFullPath);
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(e.ToString());
                        return;
                    }
                }
            }
            else
            {
                Debug.Log("请选择.xls|.xlsx文件");
            }
        }

        [MenuItem("Assets/Excel/CreateAsset")]
        public static void CreateAsset()
        {
            configScriptsName = Selection.activeObject.name;
            ExcelToAsset();
        }

        [MenuItem("Assets/Excel/CreateAssetMatchTargetType")]
        public static void CreateAssetMatchTargetType()
        {
            var fileName = Selection.activeObject.name;
            var reg = new Regex("[0-9]+?");
            configScriptsName = reg.Replace(fileName, "");
            ExcelToAsset();
        }

        private static void ExcelToAsset()
        {
            var prefix = "Assets";
            var path = AssetDatabase.GetAssetPath(Selection.activeObject);
            if (path.EndsWith(".xls") || path.EndsWith(".xlsx"))
            {
                var fullPath = Application.dataPath + path.Substring(prefix.Length);
                using (FileStream stream = File.Open(fullPath, FileMode.Open))
                {
                    IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);

                    DataSet result = excelReader.AsDataSet();
                    var collect = result.Tables[SHEET_NAME].Rows;
                    if (collect == null)
                    {
                        Debug.LogError("默认读取" + SHEET_NAME + "表格，未找到改表格");
                        return;
                    }

                    var savePath = $"{Application.dataPath}/{GameConst.PACKAGE_PATH}/{GameConst.EXCEL_PATH}/{Selection.activeObject.name}";
                    if (!Directory.Exists(savePath))
                        Directory.CreateDirectory(savePath);

                    try
                    {
                        var config = DeserializeExcel(collect);

                        var assetFullPath = $"Assets/{GameConst.PACKAGE_PATH}/{GameConst.EXCEL_PATH}/{Selection.activeObject.name}/{Selection.activeObject.name}.asset";
                        var asset = AssetDatabase.LoadAssetAtPath(assetFullPath, typeof(ScriptableObject));
                        if (asset != null)
                            AssetDatabase.DeleteAsset(assetFullPath);

                        AssetDatabase.CreateAsset(config, assetFullPath);
                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                        //Selection.activeObject = config;
                        Debug.Log("生成成功:" + assetFullPath);
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(e.ToString());
                        return;
                    }
                }
            }
            else
            {
                Debug.Log("请选择.xls|.xlsx文件");
            }
        }

        private static ScriptableObject DeserializeExcel(DataRowCollection collection)
        {
            // 减4行，第一行字段名，第二行描述, 第三行类型，第4行拓展
            var titleNum = 4;

            DealHandler dealData = new DealHandler();

            var title = collection[0];
            for (int i = 0; i < title.ItemArray.Length; i++)
            {
                var cur = title.ItemArray[i];
                if (string.IsNullOrEmpty(cur.ToString()))
                    break;
                dealData.rowKeyIndex[cur.ToString()] = i;
            }

            var config = ScriptableObject.CreateInstance(configScriptsName);
            var type = config.GetType();
            var configsInfo = type.GetField("configs", BindingFlags.Public | BindingFlags.Instance);

            // 从上往下碰到的第一个空ID行代表数据结束
            var dataCount = 0;
            for (int i = titleNum; i < collection.Count; i++)
            {
                if (string.IsNullOrEmpty(collection[i].ItemArray[0].ToString().Trim()))
                    break;
                dataCount++;
            }

            var propertyType = configsInfo.FieldType.GetElementType();
            var resultList = Array.CreateInstance(propertyType, dataCount);

            var fields = propertyType.GetFields(BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < fields.Length; i++)
                dealData.action[fields[i].Name] = GetFieldHandler(fields[i]);

            for (int i = 0; i < resultList.Length; i++)
            {
                var cur = collection[i + titleNum];
                var objStr = GetRowJson(cur, dealData);
                var value = SerializeTool.Deserialize(propertyType, objStr);

                resultList.SetValue(value, i);
            }

            configsInfo.SetValue(config, resultList);
            return config;
        }

        private static string GetRowJson(DataRow data, DealHandler dealData)
        {
            var builder = new StringBuilder();

            builder.Append('{');

            foreach (var item in dealData.rowKeyIndex)
            {
                var fieldName = item.Key;

                if (!dealData.action.ContainsKey(fieldName))
                    continue;

                var index = dealData.rowKeyIndex[fieldName];
                var curRowValue = data.ItemArray[index].ToString().Trim();
                var curhandler = dealData.action[fieldName];

                builder.Append(curhandler(curRowValue));
                builder.Append(',');
            }

            if(builder.Length > 1)
                builder.Remove(builder.Length - 1, 1);

            builder.Append('}');

            return builder.ToString();
        }

        public class DealHandler
        {
            public Dictionary<string, int> rowKeyIndex = new Dictionary<string, int>();
            public Dictionary<string, Func<string, string>> action = new Dictionary<string, Func<string, string>>();
        }

        public static Func<string, string> GetFieldHandler(FieldInfo info)
        {
            var type = info.FieldType;
            if (type.IsValueType && type.Assembly == typeof(int).Assembly)
                return new Func<string, string>((cell) => ValueFieldHandler(info, cell));

            if (type == typeof(string))
                return new Func<string, string>((cell) => StringFieldHandler(info, cell));

            if (type.IsArray || typeof(IList).IsAssignableFrom(type))
                return new Func<string, string>((cell) => ArrFieldHandler(info, cell));

            if (typeof(IDictionary).IsAssignableFrom(type))
                return new Func<string, string>((cell) => DicFieldHandler(info, cell));

            if (typeof(object).IsAssignableFrom(type))
                return new Func<string, string>((cell) => ObjFieldHandler(info, cell));

            Debug.LogError("not support!" + type);
            return null;
        }

        public static string ValueFieldHandler(FieldInfo info, string cell)
        {
            var builder = new StringBuilder();
            builder.Append(info.Name);
            builder.Append(':');
            var result = ValueHandler(cell);
            builder.Append(result);
            return builder.ToString();
        }

        public static string ValueHandler(string cell)
        {
            if (string.IsNullOrEmpty(cell))
                return "0";

            return cell;
        }

        public static string StringFieldHandler(FieldInfo info, string cell)
        {
            var builder = new StringBuilder();
            builder.Append(info.Name);
            builder.Append(':');
            var result = StringHandler(cell);
            builder.Append(result);
            return builder.ToString();
        }

        public static string StringHandler(string row)
        {
            var builder = new StringBuilder();
            builder.Append('"');
            builder.Append(row);
            builder.Append('"');
            return builder.ToString();
        }

        public static Func<string, string> GetHandler(Type type)
        {
            if (type.IsValueType && type.Assembly == typeof(int).Assembly)
                return ValueHandler;

            if (type == typeof(string))
                return StringHandler;

            if (typeof(object).IsAssignableFrom(type))
                return new Func<string, string>((cell) => ObjTypeHandler(type, cell));

            Debug.Log("不支持该类型：" + type);
            return null;
        }

        public static string ArrFieldHandler(FieldInfo info, string cell)
        {
            var builder = new StringBuilder();
            builder.Append(info.Name);
            builder.Append(':');

            var elementType = info.FieldType.GetElementType();
            var handler = GetHandler(elementType);

            builder.Append('{');

            if (!string.IsNullOrEmpty(cell))
            {
                var strArr = cell.Split('|');
                for (int i = 0; i < strArr.Length; i++)
                {
                    var curStr = strArr[i].Trim();
                    builder.Append(handler(curStr));

                    if (i != strArr.Length - 1)
                        builder.Append(',');
                }
            }

            builder.Append('}');

            return builder.ToString();
        }

        public static string DicFieldHandler(FieldInfo info, string cell)
        {
            var builder = new StringBuilder();
            builder.Append(info.Name);
            builder.Append(':');

            builder.Append('{');

            var types = info.FieldType.GetGenericArguments();
            var keyHandler = GetHandler(types[0]);
            var valueHandler = GetHandler(types[1]);

            if (!string.IsNullOrEmpty(cell))
            {
                var strArr = cell.Split('|');

                for (int i = 0; i < strArr.Length; i++)
                {
                    var elementStr = strArr[i].Trim().Split(',');
                    if (elementStr.Length != 2)
                    {
                        Debug.LogError($"字典序元素数据个数不匹配：info.Name:{info.Name},cell:{cell}");
                        continue;
                    }

                    builder.Append('{');
                    var keyResult = keyHandler(elementStr[0].Trim());
                    builder.Append(keyResult);

                    builder.Append(',');

                    var valueResult = valueHandler(elementStr[1].Trim());
                    builder.Append(valueResult);
                    builder.Append('}');

                    if (i != strArr.Length - 1)
                        builder.Append(',');
                }
            }

            builder.Append('}');

            return builder.ToString();
        }

        public static string ObjFieldHandler(FieldInfo info, string cell)
        {
            var builder = new StringBuilder();
            builder.Append(info.Name);
            builder.Append(':');
            builder.Append('{');

            var fields = info.FieldType.GetFields(BindingFlags.Public | BindingFlags.Instance);
            Func<string, string>[] actions = new Func<string, string>[fields.Length];
            for (int i = 0; i < fields.Length; i++)
                actions[i] = GetFieldHandler(fields[i]);

            var strArr = cell.Split(',');

            for (int i = 0; i < strArr.Length && i < actions.Length; i++)
            {
                var curAction = actions[i];
                var curValue = strArr[i].Trim();
                var curResult = curAction(curValue);

                builder.Append(curResult);

                if (i != strArr.Length - 1)
                    builder.Append(',');
            }

            builder.Append('}');
            return builder.ToString();
        }

        public static string ObjTypeHandler(Type type, string cell)
        {
            var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
            Func<string, string>[] actions = new Func<string, string>[fields.Length];
            for (int i = 0; i < fields.Length; i++)
                actions[i] = GetFieldHandler(fields[i]);

            var strArr = cell.Split(',');

            var builder = new StringBuilder();

            builder.Append('{');

            for (int i = 0; i < strArr.Length && i < actions.Length; i++)
            {
                var curAction = actions[i];
                var curValue = strArr[i].Trim();
                var curResult = curAction(curValue);

                builder.Append(curResult);

                if (i != strArr.Length - 1)
                    builder.Append(',');
            }

            builder.Append('}');

            return builder.ToString();
        }
    }
}
#endif